﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HappyTemplate.BuiltInFunctions
{
	class IsNullFunction : BuiltInFunctionBase
	{
		public IsNullFunction() : base("isNull", 1, false) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetObject<object>(0) == null;
		}
	}

	class CoalesceFunction : BuiltInFunctionBase
	{
		public CoalesceFunction() : base("coalesce", 1, 255, false) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			for(int i = 0; i < arguments.Count; ++i)
			{
				object arg = arguments.GetObject<object>(i);
				if(arg != null)
					return arg;
			}
				
			return null;
		}
	}

	class IsSameInstance : BuiltInFunctionBase
	{
		public IsSameInstance() : base("isSameInstance", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetObject<object>(0) == arguments.GetObject<object>(1);
		}
	}

	class FormatNumberFunction : BuiltInFunctionBase
	{
		public FormatNumberFunction() : base("formatNumber", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return String.Format("{0:" + arguments.GetString(0) + "}", arguments.GetDouble(1));
		}
	}

	class SplitFunction : BuiltInFunctionBase
	{
		public SplitFunction() : base("split", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).Split(arguments.GetString(1).ToCharArray(),
												 StringSplitOptions.None);
		}
	}

	class FormatDateFunction : BuiltInFunctionBase
	{
		public FormatDateFunction() : base("formatDate", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return String.Format("{0:" + arguments.GetString(1) + "}", arguments.GetDateTime(0));
		}
	}

	class TrimStartFunction : BuiltInFunctionBase
	{
		public TrimStartFunction() : base("trimStart", 1) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).TrimStart();
		}
	}

	class TrimEndFunction : BuiltInFunctionBase
	{
		public TrimEndFunction() : base("trimEnd", 1) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).TrimEnd();
		}
	}

	class TrimFunction : BuiltInFunctionBase
	{
		public TrimFunction() : base("trim", 1) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).Trim();
		}
	}

	class LengthFunction : BuiltInFunctionBase
	{
		public LengthFunction() : base("length", 1) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).Length;
		}
	}

	class SubstringFunction : BuiltInFunctionBase
	{
		public SubstringFunction() : base("substring", 3) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).Substring(
				arguments.GetInt32(1),
				arguments.GetInt32(2));
		}
	}

	class LeftFunction : BuiltInFunctionBase
	{
		public LeftFunction() : base("left", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).Substring(0, arguments.GetInt32(1));
		}
	}

	class RightFunction : BuiltInFunctionBase
	{
		public RightFunction() : base("right", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			string value = arguments.GetString(0);
			return value.Substring(value.Length - arguments.GetInt32(1));
		}
	}

	class PadRightFunction : BuiltInFunctionBase
	{
		public PadRightFunction() : base("padRight", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).PadRight(arguments.GetInt32(1));
		}
	}

	class PadLeftFunction : BuiltInFunctionBase
	{
		public PadLeftFunction() : base("padLeft", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).PadLeft(arguments.GetInt32(1));
		}
	}

	class IndexOfFunction : BuiltInFunctionBase
	{
		public IndexOfFunction() : base("indexOf", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).IndexOf(arguments.GetString(1));
		}
	}

	class LastIndexOfFunction : BuiltInFunctionBase
	{
		public LastIndexOfFunction() : base("lastIndexOf", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).LastIndexOf(arguments.GetString(1));
		}
	}

	class ContainsFunction : BuiltInFunctionBase
	{
		public ContainsFunction() : base("contains", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).Contains(arguments.GetString(1));
		}
	}


	class InsertFunction : BuiltInFunctionBase
	{
		public InsertFunction() : base("insert", 3) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).Insert(arguments.GetInt32(2), arguments.GetString(1));
		}
	}


	class RepeatFunction : BuiltInFunctionBase
	{
		public RepeatFunction() : base("repeat", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			string r = arguments.GetString(0);
			int n = arguments.GetInt32(1);

			switch (n)
			{
				case 0:
					return "";
				case 1:
					return r;
				default:
					StringBuilder sb = new StringBuilder(r.Length * n);
					for (int i = 0; i < n; ++i)
						sb.Append(r);
					return sb.ToString();
			}
		}
	}

	class ToLowerFunction : BuiltInFunctionBase
	{
		public ToLowerFunction() : base("toLower", 1) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).ToLower();
		}
	}

	class ToUpperFunction : BuiltInFunctionBase
	{
		public ToUpperFunction() : base("toUpper", 1) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).ToUpper();
		}
	}

	class InvertCaseFunction : BuiltInFunctionBase
	{
		public InvertCaseFunction() : base("invertCase", 1) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			char[] input = arguments.GetString(0).ToCharArray();
			char[] output = new char[input.Length];

			for (int i = 0; i < input.Length; ++i)
				if (Char.IsUpper(input[i]))
					output[i] = Char.ToLower(input[i]);
				else
					output[i] = Char.ToUpper(input[i]);

			return new String(output);
		}
	}

	class ReverseFunction : BuiltInFunctionBase
	{
		public ReverseFunction() : base("reverse", 1) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			char[] input = arguments.GetString(0).ToCharArray();
			char[] output = new char[input.Length];

			for (int i = 0; i < input.Length; ++i)
				output[i] = input[input.Length - i - 1];

			return new String(output);
		}
	}

	class ReplaceFunction : BuiltInFunctionBase
	{
		public ReplaceFunction() : base("replace", 3) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).Replace(arguments.GetString(1), arguments.GetString(2));
		}
	}

	class StartsWithFunction : BuiltInFunctionBase
	{
		public StartsWithFunction() : base("startsWith", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).StartsWith(arguments.GetString(1));
		}
	}

	class EndsWithFunction : BuiltInFunctionBase
	{
		public EndsWithFunction() : base("endsWith", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return arguments.GetString(0).EndsWith(arguments.GetString(1));
		}
	}

	class IsRegexMatchFunction : BuiltInFunctionBase
	{
		public IsRegexMatchFunction() : base("isRegexMatch", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return RegexCache.GetRegex(arguments.GetString(1)).IsMatch(arguments.GetString(0));
		}
	}
	class RegexSplitFunction : BuiltInFunctionBase
	{
		public RegexSplitFunction() : base("regexSplit", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return RegexCache.GetRegex(arguments.GetString(1)).Split(arguments.GetString(0));
		}
	}

	class RegexReplaceFunction : BuiltInFunctionBase
	{
		public RegexReplaceFunction() : base("regexReplace", 3) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return RegexCache.GetRegex(arguments.GetString(1)).Replace(arguments.GetString(0), arguments.GetString(2));
		}
	}

	class GetRegexMatchFunction : BuiltInFunctionBase
	{
		public GetRegexMatchFunction() : base("getRegexMatch", 3) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return RegexCache.GetRegex(arguments.GetString(1)).Matches(arguments.GetString(0))[arguments.GetInt32(2)];
		}
	}

	class IsEqualFunction : BuiltInFunctionBase
	{
		public IsEqualFunction() : base("isEqual", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return OmniCompare.Compare(arguments.GetObject<object>(0), arguments.GetObject<object>(1)) == 0;
		}
	}

	class IsOneOfFunction : BuiltInFunctionBase
	{
		public IsOneOfFunction() : base("isOneOf", 3, 255) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			for(int i = 1; i < arguments.Count; ++i)
				if(OmniCompare.Compare(arguments.GetObject<object>(0), arguments.GetObject<object>(i)) == 0)
					return true;

			return false;
		}
	}


	class IsGreaterFunction : BuiltInFunctionBase
	{
		public IsGreaterFunction() : base("isGreater", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return OmniCompare.Compare(arguments.GetObject<object>(0), arguments.GetObject<object>(1)) > 0;
		}
	}

	class IsLessFunction : BuiltInFunctionBase
	{
		public IsLessFunction() : base("isLess", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return OmniCompare.Compare(arguments.GetObject<object>(0), arguments.GetObject<object>(1)) < 0;
		}
	}


	class IsGreaterOrEqualFunction : BuiltInFunctionBase
	{
		public IsGreaterOrEqualFunction() : base("isGreaterOrEqual", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return OmniCompare.Compare(arguments.GetObject<object>(0), arguments.GetObject<object>(1)) >= 0;
		}
	}

	class IsLessOrEqualFunction : BuiltInFunctionBase
	{
		public IsLessOrEqualFunction() : base("isLessOrEqual", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			return OmniCompare.Compare(arguments.GetObject<object>(0), arguments.GetObject<object>(1)) <= 0;
		}
	}

	class AndFunction : BuiltInFunctionBase
	{
		public AndFunction() : base("and", 2, 255) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			for(int i = 0; i < arguments.Count; ++i)
				if(!TemplateHelp.IsTrue(arguments.GetObject<object>(i)))
					return false;

			return true;
		}
	}

	class OrFunction : BuiltInFunctionBase
	{
		public OrFunction() : base("or", 2, 255) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			for(int i = 0; i < arguments.Count; ++i)
				if(TemplateHelp.IsTrue(arguments.GetObject<object>(i)))
					return true;

			return false;
		}
	}

	class XOrFunction : BuiltInFunctionBase
	{
		public XOrFunction() : base("xor", 2, 255) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			bool retval = false;

			for (int i = 0; i < arguments.Count; ++i)
				if (TemplateHelp.IsTrue(arguments.GetObject<object>(i)))
					if (retval)
					{
						retval = false;
						break;
					}
					else
						retval = true;

			return retval;
		}
	}

	class ListFunction : BuiltInFunctionBase
	{
		public ListFunction	() : base("newList", 1, 255) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			List<object> list = new List<object>();
			for(int i = 0; i < arguments.Count;++i)
				list.Add(arguments.GetObject<object>(i));

			return list;
		}
	}

	class AppendListFunction : BuiltInFunctionBase
	{
		public AppendListFunction() : base("appendList", 2, 255) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			List<object> list = arguments.GetObject<List<object>>(0);
			for (int i = 1; i < arguments.Count; ++i)
				list.Add(arguments.GetObject<object>(i));
			return null;
		}
	}

	class IsInListFunction : BuiltInFunctionBase
	{
		public IsInListFunction() : base("isInList", 2) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			object value = arguments.GetObject<object>(0); 
			IEnumerable list = arguments.GetObject<IEnumerable>(1);

			foreach(object o in list)
				if (OmniCompare.Compare(value, o) == 0)
					return true;

			return false;
		}
	}

	class Sequence : IEnumerable<int> 
	{
		public int From { get; private set; }
		public int To { get; private set; }
		public int Step { get; private set; }

		public Sequence(int from, int to) 
			: this(from, to, 1)
		{
		}

		public Sequence(int from, int to, int step)
		{
			this.From = from;
			this.To = to;
			this.Step = step;
		}

		public IEnumerator<int> GetEnumerator()
		{
			return new SequenceEnumerator(this.From, this.To, this.Step);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}

	class SequenceEnumerator : IEnumerator<int>
	{
		readonly int _from;
		int _current;
		readonly int _to;
		readonly int _step;

		internal SequenceEnumerator(int from, int to, int step)
		{
			_from = from;
			_to = to;
			_step = step;

			//Force absolute value for step
			if (_step < 0)
				_step = -_step;
				
			//Then decide if we need an ascending or descending step.
			if(_from > _to)
				_step = -step;

			Reset();
		}

		public void Dispose()
		{
			
		}

		public bool MoveNext()
		{
			_current += _step;

			//Ascending sequences
			if(_to > _from)
				return _current <= _to;

			//Descending sequences
			if(_to < _from)
				return _current >= _to;
			
			return true;
		}

		public void Reset()
		{
			_current = _from - _step;
		}

		public int Current
		{
			get { return _current; }
		}

		object IEnumerator.Current
		{
			get { return Current; }
		}
	}

	class SequenceFunction : BuiltInFunctionBase
	{
		public SequenceFunction() : base("sequence", 2, 3) { }

		public override object Invoke(ArgumentCollection arguments)
		{
			int from = arguments.GetInt32(0);
			int to = arguments.GetInt32(1);
			int step = arguments.Count == 3 ? arguments.GetInt32(2) : 1;

			return new Sequence(from, to, step);
		}
	}

	//class IsGreaterFunction : BuiltInFunctionBase
	//{
	//    public IsGreaterFunction() : base("isGreater", 2) { }

	//    public override object Invoke(ArgumentCollection arguments)
	//    {
	//        if(arguments[0].GetType().IsPrimitive)
	//        return arguments.GetString(0).CompareTo(arguments.GetString(1));
	//    }
	//}
}
